/*
dead6809 version 1.00

started december 2007

copyright 2007-2008 Dead_Body

update jan 06, 2008
	* many bugs fixed, none left that i can find, now
	  all i need to do is code cleanup

update jan 07, 2008
	* there is a bug in sex, cant figure out why, it seems correct

*/

#include "StdAfx.h"
#include "Dead6809.h"

#define F_CARRY		0x01
#define F_OVERFLOW	0x02
#define F_ZERO			0x04
#define F_NEGATIVE	0x08
#define F_IRQ			0x10
#define F_HALFCARRY	0x20
#define F_FIRQ			0x40

#define A acc.b.A
#define B acc.b.B
#define D acc.D

static int INT_NMI = 1;
static int INT_IRQ = 2;
static int INT_FIRQ = 4;

static unsigned char c6809Cycles[256] =
{
6,0,0,6,6,0,6,6,6,6,6,0,6,6,3,6,
1,1,2,4,0,0,5,9,0,2,3,0,3,2,8,6,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
4,4,4,4,5,5,5,5,0,5,3,6,9,11,0,19,
2,0,0,2,2,0,2,2,2,2,2,0,2,2,0,2,
2,0,0,2,2,0,2,2,2,2,2,0,2,2,0,2,
6,0,0,6,6,0,6,6,6,6,6,0,6,6,3,6,
7,0,0,7,7,0,7,7,7,7,7,0,7,7,4,7,
2,2,2,4,2,2,2,0,2,2,2,2,4,7,3,0,
4,4,4,6,4,4,4,4,4,4,4,4,6,7,5,5,
4,4,4,6,4,4,4,4,4,4,4,4,6,7,5,5,
5,5,5,7,5,5,5,5,5,5,5,5,7,8,6,6,
2,2,2,4,2,2,2,0,2,2,2,2,3,0,3,0,
4,4,4,6,4,4,4,4,4,4,4,4,5,5,5,5,
4,4,4,6,4,4,4,4,4,4,4,4,5,5,5,5,
5,5,5,7,5,5,5,5,5,5,5,5,6,6,6,6
};

static char *OpcodeTable[256] =
{
"NEG ","??  ","??  ","COM ","LSR ","??  ","ROR ","ASR ","ASL ","ROL ","DEC ","??  ","INC ","TST ","JMP ","CLR ",
"--  ","--  ","NOP ","SYNC","??  ","??  ","LBRA","LBSR","??  ","DAA ","ORCC","??  ","ANDC","SEX ","EXG ","TFR ",
"BRA ","BRN ","BHI ","BLS ","BCC ","BLO ","BNE ","BEQ ","BVC ","BVS ","BPL ","BMI ","BGE ","BLT ","BGT ","BLE ",
"LEAX","LEAY","LEAS","LEAU","PSHS","PULS","PSHU","PULU","??  ","RTS ","ABX ","RTI ","CWAI","MUL ","??  ","SWI ",
"NEGA","??  ","??  ","COMA","LSRA","??  ","RORA","ASRA","ASLA","ROLA","DECA","??  ","INCA","TSTA","??  ","CLRA",
"NEGB","??  ","??  ","COMB","LSRB","??  ","RORB","ASRB","LSLB","ROLB","DECB","??  ","INCB","TSTB","??  ","CLRB",
"NEG ","??  ","??  ","COM ","LSR ","??  ","ROR ","ASR ","ASL ","ROL ","DEC ","??  ","INC ","TST ","JMP ","CLR ",
"NEG ","??  ","??  ","COM ","LSR ","??  ","ROR ","ASR ","ASL ","ROL ","DEC ","??  ","INC ","TST ","JMP ","CLR ",
"SUBA","CMPA","SBCA","SUBD","ANDA","BITA","LDA ","??  ","EORA","ADCA","ORA ","ADDA","CMPX","BSR ","LDX ","??  ",
"SUBA","CMPA","SBCA","SUBD","ANDA","BITA","LDA ","STA ","EORA","ADCA","ORA ","ADDA","CMPX","JSR ","LDX ","STX ",
"SUBA","CMPA","SBCA","SUBD","ANDA","BITA","LDA ","STA ","EORA","ADCA","ORA ","ADDA","CMPX","JSR ","LDX ","STX ",
"SUBA","CMPA","SBCA","SUBD","ANDA","BITA","LDA ","STA ","EORA","ADCA","ORA ","ADDA","CMPX","JSR ","LDX ","STX ",
"SUBB","CMPB","SBCB","ADDD","ANDB","BITB","LDB ","??  ","EORB","ADCB","ORB ","ADDB","LDD ","??  ","LDU ","??  ",
"SUBB","CMPB","SBCB","ADDD","ANDB","BITB","LDB ","STB ","EORB","ADCB","ORB ","ADDB","LDD ","STD ","LDU ","STU ",
"SUBB","CMPB","SBCB","ADDD","ANDB","BITB","LDB ","STB ","EORB","ADCB","ORB ","ADDB","LDD ","STD ","LDU ","STU ",
"SUBB","CMPB","SBCB","ADDD","ANDB","BITB","LDB ","STB ","EORB","ADCB","ORB ","ADDB","LDD ","STD ","LDU ","STU "
};

CDead6809::CDead6809()
{
}

CDead6809::~CDead6809()
{
}

void CDead6809::Init()
{
int i;

for(i=0;i<32;i++)
	{
	OpPages[i] =
	ReadPages[i] =
	WritePages[i] = 0;
	}
}

void CDead6809::Reset()
{
PC = ReadWord(0xFFFE);
D = 0;
X = 0;
Y = 0;
U = 0;
S = 0;
DP = 0;
CC = 0x50;
Cycle = 0;
Interrupts = 0;
WaitInterrupt = 0;
}

void CDead6809::FIRQ()
{
Interrupts |= INT_FIRQ;
}

void CDead6809::IRQ()
{
Interrupts |= INT_IRQ;
}

void CDead6809::NMI()
{
Interrupts |= INT_NMI;
}

#include "Dead6809funcs.cpp"				//class members not found here
#include "Dead6809ops.cpp"					//opcodes used in Step()

void CDead6809::Step()
{
u8 Opcode,Opcode2;							//current opcode
u8 tmp,ntmp,otmp;								//temp variables used by opcodes
u32 tmp2,tmp3;

CHECK_INTERRUPTS;
if(WaitInterrupt)
	{
	Cycle++;
	return;
	}
SavePC = PC;
Opcode = OpRead(PC++);
switch(Opcode)
	{
	#include "Dead6809switch.cpp"
	}
Cycle += c6809Cycles[Opcode];
}

u32 CDead6809::Execute(u32 c)
{
u32 c1 = Cycle + c;
u32 c2 = Cycle;

while(Cycle < c1)
	Step();
return(Cycle - c2);
}


void CDead6809::SaveState(CState *s)
{
s->Write16(PC);
s->Write16(D);
s->Write16(X);
s->Write16(Y);
s->Write16(U);
s->Write16(S);
s->Write8(DP);
s->Write8(CC);
s->Write16(EA);
s->Write32(Cycle);
s->Write32(Interrupts);
s->Write32(WaitInterrupt);
s->Write16(SavePC);
}

void CDead6809::LoadState(CState *s)
{
PC = s->Read16();
D = s->Read16();
X = s->Read16();
Y = s->Read16();
U = s->Read16();
S = s->Read16();
DP = s->Read8();
CC = s->Read8();
EA = s->Read16();
Cycle = s->Read32();
Interrupts = s->Read32();
WaitInterrupt = s->Read32();
SavePC = s->Read16();
}

#define ADDR_SHIFT	11
#define ADDR_MASK		0x7FF

u8 CDead6809::OpRead(u32 addr)
{
if(addr >= 0x10000)
	{
	message("op read invalid memory address '$%X', cycle %ld, PC = $%04X\n",addr,Cycle,PC);
	addr &= 0xFFFF;
	}
if(OpPages[addr >> ADDR_SHIFT])
	return(OpPages[addr >> ADDR_SHIFT][addr & ADDR_MASK]);
return(OpPtr(User,addr));
}

u8 CDead6809::Read(u32 addr)
{
//if(addr == 0x9867 || addr == 0x9815)
//	message("dead 6809: read $%04X = $%02X\n",addr,ReadPtr(User,addr));
if(addr >= 0x10000)
	{
	message("read invalid memory address '$%X', cycle %ld, PC = $%04X\n",addr,Cycle,PC);
	addr &= 0xFFFF;
	}
if(ReadPages[addr >> ADDR_SHIFT])
	return(ReadPages[addr >> ADDR_SHIFT][addr & ADDR_MASK]);
return(ReadPtr(User,addr));
}

void CDead6809::Write(u32 addr,u8 data)
{
//if(addr == 0x9867 || addr == 0x9815)
//	message("dead 6809: write $%04X = $%02X\n",addr,data);
if(addr >= 0x10000)
	{
	message("write invalid memory address '$%X', cycle %ld, PC = $%04X\n",addr,Cycle,PC);
	addr &= 0xFFFF;
	}
if(WritePages[addr >> ADDR_SHIFT])
	WritePages[addr >> ADDR_SHIFT][addr & ADDR_MASK] = data;
else
	WritePtr(User,addr,data);
}

u16 CDead6809::ReadWord(u32 addr)
{
u16 ret = 0;

ret |= Read(addr + 0) << 8;
ret |= Read(addr + 1) << 0;
return(ret);
}

void CDead6809::WriteWord(u32 addr,u16 data)
{
Write(addr + 0,(u8)(data >> 8) & 0xFF);
Write(addr + 1,(u8)(data >> 0) & 0xFF);
}

void CDead6809::SetUser(void *u)
{
User = u;
}

void CDead6809::SetPage(int p,u8 *d)
{
OpPages[p] = d;
ReadPages[p] = d;
WritePages[p] = d;
}

void CDead6809::SetOpPage(int p,u8 *d)
{
OpPages[p] = d;
}

void CDead6809::SetReadPage(int p,u8 *d)
{
ReadPages[p] = d;
}

void CDead6809::SetWritePage(int p,u8 *d)
{
WritePages[p] = d;
}

void CDead6809::SetOp(ReadFunc *r)
{
OpPtr = r;
}

void CDead6809::SetRead(ReadFunc *r)
{
ReadPtr = r;
}

void CDead6809::SetWrite(WriteFunc *w)
{
WritePtr = w;
}

//disassembler stuff
enum c6809addrmodes_e
	{
	c6809_addr_none = 0,
	c6809_addr_implied,
	c6809_addr_immediate,
	c6809_addr_immediate8,
	c6809_addr_direct,
	c6809_addr_indexed,
	c6809_addr_extended,
	c6809_addr_relative,
	c6809_addr_relative16
	};

static int GetAddrMode(u8 Opcode)
{
switch(Opcode & 0xF0)
	{
	case 0x00:
		return(c6809_addr_direct);
	case 0x10:
		if(Opcode == 0x16 || Opcode == 0x17)
			return(c6809_addr_relative16);
		if(Opcode == 0x1E || Opcode == 0x1F)
			return(c6809_addr_immediate8);
		break;
	case 0x20:
		return(c6809_addr_relative);
	case 0x30:
		if(Opcode < 0x34)
			return(c6809_addr_indexed);
		else if(Opcode >= 0x34 && Opcode <= 0x37)
			return(c6809_addr_immediate8);
		else if(Opcode == 0x3C)
			return(c6809_addr_immediate8);
	case 0x40:
	case 0x50:
		return(c6809_addr_implied);
	case 0x60:
		return(c6809_addr_indexed);
	case 0x70:
		return(c6809_addr_extended);
	case 0x80:
		if(Opcode == 0x8D)		//bsr, relative
			return(c6809_addr_relative);
	case 0xC0:
		return(c6809_addr_immediate8);
	case 0x90:
	case 0xD0:
		return(c6809_addr_direct);
	case 0xA0:
	case 0xE0:
		return(c6809_addr_indexed);
	case 0xB0:
	case 0xF0:
		return(c6809_addr_extended);
	}
return(c6809_addr_none);
}

u32 CDead6809::Disassemble(char *dest,u32 p)
{
u8 Opcode,N,Reg;
char *Op;
char RegList[] = "XYUS";

Opcode = OpRead(p++);
Op = OpcodeTable[Opcode];
switch(GetAddrMode(Opcode))
	{
	case c6809_addr_none:
	case c6809_addr_implied:
		sprintf(dest,"($%02X)\t%s",Opcode,Op);
		break;
	case c6809_addr_immediate:
		sprintf(dest,"($%02X)\t%s\t$%04X",Opcode,Op,ReadWord(p));
		p += 2;
		break;
	case c6809_addr_immediate8:
		sprintf(dest,"($%02X)\t%s\t$%02X",Opcode,Op,Read(p));
		p += 1;
		break;
	case c6809_addr_direct:
		sprintf(dest,"($%02X)\t%s\t<$%02X ($%04X)",Opcode,Op,Read(p),Read(p) | (DP << 8));
		p += 1;
		break;
	case c6809_addr_indexed:
		N = Read(p++);
		Reg = RegList[(N >> 5) & 3];
		if((N & 0x80) == 0)
			{
			if((N & 0x10) == 0)
				sprintf(dest,"($%02X)\t%s\t$%X,%c",Opcode,Op,N & 0xF,Reg);
			else
				sprintf(dest,"($%02X)\t%s\t-$%X,%c",Opcode,Op,N & 0xF,Reg);
			}
		else
			{
			char FF[2] = "";
			char FB[2] = "";
			
			if(N & 0x10)
				{
				strcpy(FF,"[");
				strcpy(FB,"]");
				}
			switch(N & 0xF)
				{
				case 0:	sprintf(dest,"($%02X)\t%s\t%s,%c+%s",Opcode,Op,FF,Reg,FB);	break;
				case 1:	sprintf(dest,"($%02X)\t%s\t%s,%c++%s",Opcode,Op,FF,Reg,FB);	break;
				case 2:	sprintf(dest,"($%02X)\t%s\t%s,-%c%s",Opcode,Op,FF,Reg,FB);	break;
				case 3:	sprintf(dest,"($%02X)\t%s\t%s,--%c%s",Opcode,Op,FF,Reg,FB);	break;
				case 4:	sprintf(dest,"($%02X)\t%s\t%s,%c%s",Opcode,Op,FF,Reg,FB);	break;
				case 5:	sprintf(dest,"($%02X)\t%s\t%sB,%c%s",Opcode,Op,FF,Reg,FB);	break;
				case 6:	sprintf(dest,"($%02X)\t%s\t%sA,%c%s",Opcode,Op,FF,Reg,FB);	break;
				case 8:	sprintf(dest,"($%02X)\t%s\t%s<$%02X,%c%s",Opcode,Op,FF,Read(p++),Reg,FB); break;
				case 9:	sprintf(dest,"($%02X)\t%s\t%s>$%04X,%c%s",Opcode,Op,FF,ReadWord(p),Reg,FB); p += 2; break;
				case 11:	sprintf(dest,"($%02X)\t%s\t%sD,%c%s",Opcode,Op,FF,Reg,FB);	break;
				case 12:	sprintf(dest,"($%02X)\t%s\t%s<$%02X,PC%s",Opcode,Op,FF,Read(p++),FB); break;
				case 13:	sprintf(dest,"($%02X)\t%s\t%s>$%04X,PC%s",Opcode,Op,FF,ReadWord(p),FB); p += 2; break;
				case 15:	sprintf(dest,"($%02X)\t%s\t%s$%04X%s",Opcode,Op,FF,ReadWord(p),FB); p += 2; break;
				}
			}
		break;
	case c6809_addr_extended:
		sprintf(dest,"($%02X)\t%s\t[$%02X]",Opcode,Op,ReadWord(p));
		p += 2;
		break;
	case c6809_addr_relative:
		sprintf(dest,"($%02X)\t%s\t$%04X",Opcode,Op,p + (s8)(Read(p) + 1));
		p += 1;
		break;
	case c6809_addr_relative16:
		sprintf(dest,"($%02X)\t%s\t$%04X",Opcode,Op,p + (s16)(ReadWord(p) + 1));
		p += 2;
		break;
	}
return(p);
}

char *CDead6809::GenerateRegNames(char *dest)
{
sprintf(dest,
"pc:\nd:\nx:\ny:\nu:\ns:\ndp:\ncc:\n\n"
"vectors\n"
"xxx:\nswi3:\nswi2:\nfirq:\n"
"irq:\nswi:\nnmi:\nreset:\n"
);
return(dest);
}

#undef D

char *CDead6809::GenerateRegData(char *dest)
{
sprintf(dest,
"$%04X\n$%04X\n$%04X\n$%04X\n$%04X\n$%04X\n$%02X\n$%02X\n\n\n"
"$%04X\n$%04X\n$%04X\n$%04X\n"
"$%04X\n$%04X\n$%04X\n$%04X\n",
	PC,acc.D,X,Y,U,S,DP,CC,
	ReadWord(0xFFF0),ReadWord(0xFFF2),ReadWord(0xFFF4),ReadWord(0xFFF6),
	ReadWord(0xFFF8),ReadWord(0xFFFA),ReadWord(0xFFFC),ReadWord(0xFFFE)
);
return(dest);
}

u32 CDead6809::GetPC()
{
return(PC);
}

u32 CDead6809::GetCycle()
{
return(Cycle);
}
